package org.mullin.stratego.client;

import java.util.Collection;
import java.util.Map;

import org.mullin.stratego.shared.Color;
import org.mullin.stratego.shared.Coord;
import org.mullin.stratego.shared.Move;
import org.mullin.stratego.shared.Piece;
import org.mullin.stratego.shared.Rank;
import org.mullin.stratego.shared.State;

import com.google.gwt.core.shared.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DragOverEvent;
import com.google.gwt.event.dom.client.DragOverHandler;
import com.google.gwt.event.dom.client.DragStartEvent;
import com.google.gwt.event.dom.client.DragStartHandler;
import com.google.gwt.event.dom.client.DropEvent;
import com.google.gwt.event.dom.client.DropHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;

/**
 * Implementation of the view. Handles visual representation of the state.
 * 
 * @author kevin.mullin
 *
 */
public class Graphics extends Composite implements Presenter.View {
	
	public static GameImages gameImages = GWT.create(GameImages.class);
	private static GraphicsUiBinder uiBinder = GWT.create(GraphicsUiBinder.class);

	interface GraphicsUiBinder extends UiBinder<Widget, Graphics> {
	}
	
	@UiField GameCss css;
	// Label listing current turn if game in progress, or winner if game over.
	@UiField Label gameStatus;
	// Game board.
	@UiField Grid gameGrid;
	// Button to login.
	@UiField Button login;
	@UiField Button refreshMatches;
	@UiField Button autoMatch;
	@UiField Button deleteMatch;
	
	@UiField Label color;
	@UiField Label turn;
	
	@UiField ListBox matches;
	
	@UiField Label email;
	@UiField Label gameStartDate;
	@UiField Label rating;
	@UiField Label opponent;
	
	@UiField Button singlePlayer;

	HandlerRegistration handlerRegistration;
	
	Map<Rank, Integer> rankCounts;	
	
	public final Presenter presenter;	
	
	public static final int LOLMAGIC = 50;	
	
	public GameMessages gameMessages = GWT.create(GameMessages.class);
	
	public Graphics() {
		
		initWidget(uiBinder.createAndBindUi(this));
		presenter = new Presenter(this);		

		gameGrid.resize(State.ROWS, State.COLS);
		gameGrid.setWidth("550px");
		gameGrid.setHeight("550px");
		gameGrid.setCellPadding(0);
		gameGrid.setCellSpacing(0);
		gameGrid.setBorderWidth(1);		
		
		// TODO: state is hardcoded at creation of presenter
		// need to take input from user to set up the board
		State s = presenter.getState();
		
		for (int i = 0; i < State.COLS; i++) {
			for (int j = 0; j < State.ROWS; j++) {
				final Image image = new Image();
				final Coord c = new Coord(i, j);
				
				// handle a click on the game board- if click results in a move,
				// update the history.
				image.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						if (presenter.clickedOn(c, true)) {							
//							History.newItem(State.serialize(presenter.state));
						}
					}					
				});
				image.setResource(getImage(s, i, j));
				image.setWidth("50px");
				image.setHeight("50px");
				image.setStyleName(gameImages.css().redPiece());
				gameGrid.setWidget(i, j, image);
			}
		}
		
		matches.addChangeHandler(new ChangeHandler() {

			@Override
			public void onChange(ChangeEvent event) {
				int index = matches.getSelectedIndex();
				if (index > 0) {
					String s = matches.getValue(index);
					System.out.println("selected item " + s + " at index " + index);
					presenter.loadMatch(s);
				}
			}
		});

		deleteMatch.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				presenter.deleteMatch();
			}
			
		});
		deleteMatch.setText(gameMessages.deleteMatch());
		
		refreshMatches.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				presenter.getMatches();
				
			}
			
		});
		refreshMatches.setText(gameMessages.refreshMatches());
		
		autoMatch.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				presenter.autoMatch();
			}
			
		});
		autoMatch.setText(gameMessages.autoMatch());
		
		setTurn(presenter.getState().getTurn());
		gameStatus.setText("Waiting for opponent.");
		
		singlePlayer.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				presenter.singlePlayer();
			}
			
		});
	}
	

	/**
	 * Attach drag and drop handlers to all images on the board.
	 */
	public void setupHandlers() {
		for (int i = 0; i < State.ROWS; i++) {
			for (int j = 0; j < State.COLS; j++) {
				final Image image = (Image)gameGrid.getWidget(i, j);
				final Coord c = new Coord(i, j);
				//image.getElement().setDraggable(Element.DRAGGABLE_TRUE);				
				
				// drag start handler- mark the starting coordinate.
				image.addDragStartHandler(new DragStartHandler() {
					@Override
					public void onDragStart(DragStartEvent event) {
						event.getDataTransfer().setDragImage(image.getElement(), LOLMAGIC/2, LOLMAGIC/2);
						presenter.clickedOn(c, false);
					}					
				});
				
				// drag over handler- ignore.
				image.addDragOverHandler(new DragOverHandler() {
					@Override
					public void onDragOver(DragOverEvent event) {
						event.preventDefault();
					}
				});
				
				// drop handler- try to use the ending coordinate to make a move. 
				// If it succeeds, update history.
				image.addDropHandler(new DropHandler() {
					@Override
					public void onDrop(DropEvent event) {						
						event.preventDefault();
						if (presenter.clickedOn(c, false)) {
//							History.newItem(State.serialize(presenter.state));
						}
					}					
				});
				
			}
		}
	}
	
	/**
	 * Get the image used to represent a rank.
	 * 
	 * @param r Rank of piece.
	 * @return ImageResource representing rank.
	 */
	public ImageResource getImage(Rank r) {
		switch (r) {
			case BOMB:
				return gameImages.bombCard();
			case MARSHAL:					
				return gameImages.marshalCard();
			case GENERAL:
				return gameImages.generalCard();
			case COLONEL:
				return gameImages.colonelCard();
			case MAJOR:
				return gameImages.majorCard();
			case CAPTAIN:
				return gameImages.captainCard();
			case LIEUTENANT:
				return gameImages.lieutenantCard();
			case SERGEANT:
				return gameImages.sergeantCard();
			case SAPPER:
				return gameImages.sapperCard();
			case SCOUT:
				return gameImages.scoutCard();
			case SPY:
				return gameImages.spyCard();				
			case FLAG:
				return gameImages.flagCard();
			case LAND:
				return gameImages.land();
			case WATER:
				return gameImages.water();
			default:				
				return gameImages.flagCard();
		}
	}	
	
	/**
	 * Gets the image to use for piece at coord (i, j) in the state s.
	 * 
	 * @param s State.
	 * @param i Row.
	 * @param j Column.
	 * @return Image to use for piece.
	 */
	private ImageResource getImage(State s, int i, int j) {
		// TODO: is this even needed anymore?
		Piece p = s.getPiece(new Coord(i, j));
		if (p.shown || p.color == s.getTurn()) {
			// piece should be shown- it is revealed or the current player's piece
			return getImage(p.rank);
		} else {
			// land/water or an unknown enemy piece
			switch (p.rank) {
				case WATER:
					return gameImages.water();
				case LAND:
					return gameImages.land();
				default:
					return gameImages.flippedCard();
			}
		}
	}

	/**
	 * Animate a move.
	 * 
	 * @param move
	 * @param movingPiece
	 * @param attacking
	 */
	@Override
	public void animateMove(Move move, Piece movingPiece, boolean attacking) {
		Image movingImage = (Image) gameGrid.getWidget(move.from.x, move.from.y);
		Image targetImage = (Image) gameGrid.getWidget(move.to.x, move.to.y);
		
		ImageResource pieceImage;
		if (movingPiece.shown) {
			pieceImage = getImage(movingPiece.rank);
		} else {
			pieceImage = gameImages.flippedCard();
		}
		
		ImageResource blankImage = gameImages.land();
				
		MoveAnimation moveAnimation = new MoveAnimation(movingImage, targetImage, pieceImage, blankImage);
		moveAnimation.run(1000);
	}
	
	/**
	 * Sets the image at c to correspond to the piece.
	 * 
	 * @param c Coord for update.
	 * @param p Piece to use.
	 */
	@Override
	public void setPiece(Coord c, Piece p) {
		System.out.println("setPiece- coord = " + c + " piece = " + p);
		Image image = (Image)gameGrid.getWidget(c.x, c.y);
		ImageResource resource = getImage(p.rank);
		image.setResource(resource);		
	}
	
	/**
	 * Hide piece at coord c. Sets image to a flipped card.
	 * 
	 * @param c Coord of piece to hide.
	 */
	@Override
	public void setHidden(Coord c) {		
		Image image = (Image)gameGrid.getWidget(c.x, c.y);
		image.setResource(gameImages.flippedCard());
	}
	
	/**
	 * Highlight piece at coord c.
	 * 
	 * @param c Coord of piece to highlight.
	 * @param highlighted True to turn on highlighting, false to remove.
	 */
	@Override
	public void setHighlighted(Coord c, boolean highlighted) {
		System.out.println("trying to get image at coord " + c);
		Image element = (Image)gameGrid.getWidget(c.x, c.y);
		if (element == null) {
			System.out.println("element is null.");
		}
		if (highlighted) {
			element.getElement().setClassName(css.highlighted());
		} else {
			element.getElement().removeClassName(css.highlighted());
		}
	}
	
	
	public void setColor(int color) {
		this.color.setText("You are " + Color.toString(color));
	}
	
	
	/**
	 * Set status to have current player's turn.
	 * 
	 * @param turn Player whose turn it is.
	 */
	@Override
	public void setTurn(int turn) {		
		this.turn.setText(Color.toString(turn) + "'s turn.");
	}
	
	/**
	 * Set status to have winner of game.
	 * 
	 * @param turn Winner of game.
	 */
	@Override
	public void setWinner(int turn) {
		//gameStatus.setText(Color.toString(turn) + " wins.");
	}
	
	@Override
	public void setLogButton(final String s, boolean b) {
		System.out.println("handle login/out");
		
		if (handlerRegistration != null) {
			handlerRegistration.removeHandler();
		}
		
		if (b) {
			handlerRegistration = login.addClickHandler(new ClickHandler() {

				@Override
				public void onClick(ClickEvent event) {
					LogoutPanel logoutPanel = new LogoutPanel(s);
					logoutPanel.center();
				}
				
			});
			login.setText(gameMessages.logOut());
		} else {
			handlerRegistration = login.addClickHandler(new ClickHandler() {

				@Override
				public void onClick(ClickEvent event) {
					LoginPanel loginPanel = new LoginPanel(s);
					loginPanel.center();
				}
				
			});
			login.setText(gameMessages.logIn());
		}
	}
	
	@Override
	public void setStatus(String s) {
		gameStatus.setText(s);
	}
	
	
	@Override
	public void setMatchList(Collection<String> matchList) {
		matches.clear();
		matches.addItem("");
		for (String m : matchList) {
			matches.addItem(m);
		}
	}

	@Override
	public void setEmail(String s) {
		email.setText("email = " + s);
	}

	@Override
	public void setGameStartDate(String s) {
		gameStartDate.setText("game start date = " + s);
	}
	
	@Override
	public void setPlayerRating(String s) {
		rating.setText("ranking = " + s);
	}
	
	@Override
	public void setOpponent(String s) {
		opponent.setText("opponent = " + s);
	}
}
